package com.lagou.edu.component.scan;

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.annotation.Component;
import com.lagou.edu.annotation.Repository;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.utils.AnnotationUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

/**
 * @author jiumoxiaozhi
 * @date 2021/6/19 11:17 上午
 * @description
 */
@Component
public class ComponentScan {

    public static Map<String, Object> doScan(String path) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 定义一个Map结果集，使用Map是因为需要存储ID和对象实例
        Map<String, Object> resultMap = new HashMap<>();
        // 定义一个Set集合，用来承接扫描到的Class
        Set<Class<?>> classes = new HashSet<>();

        String packageSearchPath = path.replace(".", "/");
        // 定义一个枚举的集合 并进行循环来处理这个目录下的.class文件
        Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageSearchPath);
        while(urls.hasMoreElements()){
            /**
             * 打印结果，file是协议，代表是磁盘文件。 如果是jar:/代表是jar包
             * file:/Users/jiumoxiaozhi/ideaProjects/lagou-transfer/target/classes/com/lagou/edu
             */
            URL url = urls.nextElement();
            System.out.println(url);
            if (url.getProtocol().equalsIgnoreCase("file")){
                //声明一个文件集合，用来承接查找到的文件
                List<File> files = new ArrayList<>();
                //递归查找所有文件并添加到集合中
                listFiles(new File(url.getFile()), files);
                //加载查找到的所有class文件
                filesToClass(files, classes, packageSearchPath);

            } else {
                System.out.println("非法符号");
            }
        }

        for (Class<?> aClass : classes) {
            System.out.println("className:" + aClass.getName());

            //获取注解中的别名
            String beanId;
            Service serviceAnnotation = aClass.getAnnotation(Service.class);
            Repository repositoryAnnotation = aClass.getAnnotation(Repository.class);
            Component componentAnnotation = aClass.getAnnotation(Component.class);
            //获取注解中的value属性的值
            if (serviceAnnotation != null)
                beanId = serviceAnnotation.value();
            else if (repositoryAnnotation != null)
                beanId = repositoryAnnotation.value();
            else if (componentAnnotation != null)
                beanId = componentAnnotation.value();
            else {
                System.out.println("annotation is not Service | Repository | Component");
                continue;
            }
            Object bean = aClass.newInstance();

            //如果注解别名中的value是空的，则将类名首字母小写作为别名
            if (StringUtils.isEmpty(beanId)){
                beanId = AnnotationUtils.resolveBeanId(aClass);
            }

            if (!StringUtils.isEmpty(beanId)) {
                //处理为Map维护id和实例的关系
                resultMap.put(beanId, bean);
            }
        }
        return resultMap;
    }


    private static void filesToClass(List<File> files, Set<Class<?>> classes, String packageSearchPath)
            throws ClassNotFoundException {
        for (File file : files) {
            //因为下面是文件操作，所以需要把"\"转换成"/"
            String filePath = file.getAbsolutePath().replaceAll("\\\\", "/");
            //从文件的绝对路径截取包的相对路径（com.lagou.edu.xxx.xx.class）
            String packageName = filePath.substring(filePath.lastIndexOf(packageSearchPath));
            //处理包路径名称--去掉后缀.class ，并且把"/"替换成"." 这样就是com.x.x 格式了,就可以用Class.forName加载了
            packageName = packageName.replaceAll(".class", "").replaceAll("/", ".");
            //根据类名反射获取Class
            Class<?> aClass = Class.forName(packageName);
            classes.add(aClass);
        }
    }

    /**
     * 递归遍历所有文件
     * @param dir 文件路径
     * @param files 查找到的文件集合
     */
    private static void listFiles(File dir, List<File> files) {

        if (dir.isDirectory()){
            for (File f : Objects.requireNonNull(dir.listFiles())) {
                listFiles(f, files);
            }
        } else {
            if (dir.getName().endsWith(".class"))
                files.add(dir);
        }

    }


}
